home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / DockExtenders / Locus / Source / ItemCell.m < prev    next >
Text File  |  1993-05-21  |  28KB  |  1,209 lines

  1.  
  2. /*
  3.     Copyright 1993  Jeremy Slade.  All rights reserved.
  4. */
  5.  
  6. #import "ItemCell.h"
  7.  
  8. #import "Globals.h"
  9. #import "Group.h"
  10. #import "PSWraps.h"
  11.  
  12. #import <dpsclient/psops.h>
  13. #import <dpsclient/wraps.h>
  14. #import <mach/message.h>
  15. #import <stdio.h>
  16. #import <streams/streams.h>
  17. #import <string.h>
  18. #import <sys/file.h>
  19. #import <sys/types.h>
  20.  
  21.  
  22. const char *ICModeTitles[] = {
  23.     "Unknown", "Large Browse", "Small Browse",
  24.     "Large Icon", "Small Icon",
  25.     NULL
  26. };
  27.  
  28. const char *ICFormatTitles[] = {
  29.     "Unknown", "Full Path", "File and Path",
  30.     "File Only", "Relative", "No Path",
  31.     NULL
  32. };
  33.  
  34.  
  35. // This macro is called whenever a property is set on an item
  36. // Setting a property causes a Dynamic item to become static.
  37. #define NO_LONGER_STATIC    { if ( dynamic ) [self setDynamic:NO]; }
  38.  
  39.  
  40. /*
  41.  * Defines of various drawing attributes
  42.  */
  43.  
  44.  // Things common to all modes
  45. #define X_BORDER    (4)                // Same for all modes
  46. #define Y_BORDER    (4)                // Same for all modes
  47.  
  48. // Large Browse mode
  49. #define LB_ICON_W            (48.0)
  50. #define LB_ICON_H            (48.0)
  51. #define LB_ICON_X            (X_BORDER+4)
  52. #define LB_ICON_Y            (Y_BORDER+4)
  53. #define LB_DOTS_X            (LB_ICON_X-8)
  54. #define LB_DOTS_Y            (LB_ICON_Y-8)
  55. #define LB_PATH_H            (pathHeight)
  56. #define LB_PATH_X            (LB_ICON_X + LB_ICON_W + X_BORDER)
  57. #define LB_PATH_Y            (LB_ICON_Y + LB_ICON_H - LB_PATH_H )
  58. #define LB_INFO_H            (infoHeight)
  59. #define LB_INFO1_X            LB_PATH_X
  60. #define LB_INFO1_Y            (LB_PATH_Y - LB_INFO_H)
  61. #define LB_INFO2_X            LB_PATH_X
  62. #define LB_INFO2_Y            (LB_INFO1_Y - LB_INFO_H)
  63. #define LB_WIDTH            (100)
  64. #define LB_HEIGHT            (LB_ICON_Y + LB_ICON_H + Y_BORDER)
  65.  
  66. // Small Browse mode
  67. #define SB_ICON_W            (24.0)
  68. #define SB_ICON_H            (24.0)
  69. #define SB_ICON_X            (X_BORDER)
  70. #define SB_ICON_Y            (Y_BORDER)
  71. #define SB_PATH_H            (pathHeight)
  72. #define SB_PATH_X             ( drawInfo.sb_icon ? \
  73.             (SB_ICON_X + SB_ICON_W + X_BORDER) : (X_BORDER) )
  74. #define SB_PATH_Y ( drawInfo.sb_icon ? \
  75.             (SB_ICON_Y + SB_ICON_H - SB_PATH_H) : (pathDescender + 1) )
  76. #define SB_WIDTH            (100)
  77. #define SB_HEIGHT            ( drawInfo.sb_icon ? \
  78.             (SB_ICON_Y + SB_ICON_H + Y_BORDER) \
  79.             : (SB_PATH_H + 1) )
  80.  
  81. // Large Icon mode
  82. #define LI_ICON_W            (48.0)
  83. #define LI_ICON_H            (48.0)
  84. #define LI_ICON_X            (-24.0)
  85. #define LI_ICON_Y            (24.0)
  86. #define LI_PATH_H            (pathHeight + pathDescender)
  87. #define LI_PATH_X            (0)
  88. #define LI_PATH_Y            (LI_ICON_H + 4 + LI_PATH_H - pathDescender)
  89.  
  90. // Small Icon mode
  91. #define SI_ICON_W            (24.0)
  92. #define SI_ICON_H            (24.0)
  93. #define SI_ICON_X            (-12.0)
  94. #define SI_ICON_Y            (12.0)
  95. #define SI_PATH_H            (pathHeight + pathDescender)
  96. #define SI_PATH_X            (0)
  97. #define SI_PATH_Y            (LI_ICON_H + 3 + LI_PATH_H - pathDescender)
  98.  
  99.  
  100.  
  101. // -------------------------------------------------------------------------
  102. //   Stuff used in drawing
  103. // -------------------------------------------------------------------------
  104.  
  105. DrawInfo drawInfo;    // Global structure of current drawing settings
  106. int    nameMode;        // Current nameMode
  107. id    pathFont;        // Font used to draw PATH LINE
  108. id    infoFont;        // Font used to draw INFO LINEs
  109. id    dots;            // Tri-dots icon
  110. float pathHeight;
  111. float pathDescender;
  112. float infoHeight;
  113. float infoDescender;
  114.  
  115. static id    iconTable = nil;    // HashTable of Icons
  116.  
  117.  
  118. // The following Category of speaker declares the interface for a method
  119. // that was supported in NS 2.x, but is not longer officially supported
  120. // in NS 3.0, but of course it has to still be there in order for 2.x
  121. // apps to run under 3.0, So it is ok for us to call it...
  122. // (there's no garauntee of how long this will be true)
  123. @interface Speaker ( ItemCell_Undocumented )
  124. - (int)launchProgram:(const char *)app ok:(int *)flag;
  125. @end
  126.  
  127.  
  128. @implementation ItemCell // ------------------------------------------------
  129.  
  130.  
  131. // -------------------------------------------------------------------------
  132. //   Creating, initializing methods
  133. // -------------------------------------------------------------------------
  134.  
  135.  
  136. + initialize
  137. /*
  138.     Set the class version number
  139.     Set up the objects that are used by all instances to do drawing.
  140. */
  141. {
  142.     NXCoord  ascender, descender, lineHeight;
  143.     
  144.     // Set class version
  145.     [self setVersion:ItemCell_VERSION];
  146.     
  147.     // Font used for drawing the Path line
  148.     pathFont = [Font newFont:"Helvetica" size:12 matrix:NX_FLIPPEDMATRIX];
  149.     NXTextFontInfo ( pathFont, &ascender, &descender, &lineHeight );
  150.     pathHeight = lineHeight;
  151.     pathDescender = descender;
  152.     
  153.     // Font used for drawing the Info lines
  154.     infoFont = [Font newFont:"Helvetica" size:12 matrix:NX_FLIPPEDMATRIX];
  155.     NXTextFontInfo ( infoFont, &ascender, &descender, &lineHeight );
  156.     infoHeight = lineHeight;
  157.     infoDescender = descender;
  158.     
  159.     // Tri-dots image to show App's running status
  160.     dots = [NXImage findImageNamed:"dots"];
  161.     
  162.     // Set up the iconTable
  163.     // Keys are the name of the icon, and the value is the id of the
  164.     // NXImage that contains the actual icon
  165.     iconTable = [[HashTable alloc] initKeyDesc:"*" valueDesc:"@"];
  166.     
  167.     return ( self );
  168. }
  169.  
  170.  
  171.  
  172. - init
  173. {
  174.     return ( [self initPath:NULL] );
  175. }
  176.  
  177.  
  178.  
  179. - initPath:(const char *)aPath
  180. /*
  181.     Designated initializer: initialize an instance and set it's path to aPath
  182. */
  183. {
  184.     [super init];
  185.     
  186.     [self setPath:aPath];
  187.  
  188.     isAutoLaunch = isGroupLaunch = NO;
  189.     
  190.     fileType = FT_UNKNOWN;
  191.  
  192.     specificApp = NO;
  193.     createBrowser = YES;
  194.     hideIcon = NO;
  195.     dynamic = NO;
  196.     
  197.     return ( self );
  198. }
  199.  
  200.  
  201.  
  202. - free
  203. /*
  204.     Free the instance and all support objects it allocated
  205. */
  206. {
  207.     if ( path ) NX_FREE ( path );
  208.     if ( fileStr && fileStr !=path ) NX_FREE ( fileStr );
  209.     if ( actualImage ) [actualImage free];
  210.     if ( appName ) NX_FREE ( appName );
  211.     if ( rootAt ) NX_FREE ( rootAt );
  212.     
  213.     return ( [super free] );
  214. }
  215.  
  216.  
  217.  
  218. // -------------------------------------------------------------------------
  219. //   Drawing Methods
  220. // -------------------------------------------------------------------------
  221.  
  222.  
  223. + setDrawInfo:(const DrawInfo *)info
  224. /*
  225.     Set the current global draw info.  All drawing is done based on the current draw info, so a group that is going to display must first call this method to make its draw info current.
  226. */
  227. {
  228.     drawInfo = *info;
  229.     
  230.     // Determine the nameMode (so this doesn't have to be done each time
  231.     // -fileStr is called)
  232.     switch ( drawInfo.mode ) {
  233.         case IC_LARGE_BROWSE:
  234.             nameMode = drawInfo.lb_nameMode; break;
  235.         case IC_SMALL_BROWSE:
  236.             nameMode = drawInfo.sb_nameMode; break;
  237.         case IC_LARGE_ICON:
  238.         case IC_SMALL_ICON:
  239.         default:
  240.             nameMode = IC_FILEONLY; break;
  241.     }
  242.  
  243.     return ( self );
  244. }
  245.  
  246.  
  247.  
  248. + getDrawInfo:(DrawInfo *)info
  249. {
  250.     *info = drawInfo;
  251.     return ( self );
  252. }
  253.  
  254.  
  255.  
  256. + (int)drawMode
  257. {
  258.     return ( drawInfo.mode );
  259. }
  260.  
  261.  
  262.  
  263. - calcCellSize:(NXSize *)size;
  264. /*
  265.     Calculate the minimum size needed to draw the cell based in the current draw info.
  266. */
  267. {
  268.     switch ( drawInfo.mode ) {
  269.         case IC_LARGE_BROWSE:
  270.             size->width = LB_WIDTH;
  271.             size->height = LB_HEIGHT;
  272.             break;
  273.         case IC_SMALL_BROWSE:
  274.             size->width = SB_WIDTH;
  275.             size->height = SB_HEIGHT;
  276.             break;
  277.         case IC_LARGE_ICON: // Shouldn't ever get called for this mode
  278.         case IC_SMALL_ICON: // Shouldn't ever get called for this mode
  279.         default:
  280.             size->width = 0;
  281.             size->height = 0;
  282.             break;
  283.     }
  284.     
  285.     //if ( DEBUGGING ) printf ( "calcCellSize: %.0f, %.0f\n", size->width, size->height );
  286.     return ( self );
  287. }
  288.  
  289.  
  290.  
  291. - drawSelf:(const NXRect *)cellFrame inView:controlView
  292. {
  293.     [self drawInside:cellFrame inView:controlView];
  294.     return ( self );
  295. }
  296.  
  297.  
  298.  
  299. - drawInside:(const NXRect *)cellFrame inView:controlView
  300. /*
  301.     Draw only the interior of the cell.  NOTE: For all Y coords used in drawing, it assumes that controlView will be flipped ( [controlView isFlipped] == YES ).  This is because it assumes that it will always be drawn in a Matrix (or subclass), which is always flipped.
  302. */
  303. {
  304.     NXRect rects[2];
  305.     NXPoint pt;
  306.     const char *infoStr;
  307.     
  308.     if ( (drawInfo.mode == IC_LARGE_BROWSE ||
  309.             drawInfo.mode == IC_SMALL_BROWSE) ) {
  310.         // Erase the cell 
  311.         PSsetgray ( (cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY );
  312.         NXRectFill ( cellFrame );
  313.     
  314.         // Draw a dark line above and below the cell if it is selected
  315.         if ( (cFlags1.state || cFlags1.highlighted) ) {
  316.             PSsetgray ( NX_DKGRAY );
  317.             rects[0].origin.x = NX_X(cellFrame);
  318.             rects[0].origin.y = NX_Y(cellFrame);
  319.             rects[0].size.width = NX_WIDTH(cellFrame);
  320.             rects[0].size.height = 1.0;
  321.             rects[1] = rects[0];
  322.             rects[1].origin.y = NX_MAXY(cellFrame) - 1.0;
  323.             NXRectFillList ( rects, 2 );
  324.         }
  325.     }
  326.  
  327.     [self fileType];    // Make sure we have a fileType
  328.  
  329.     // Draw according to drawInfo
  330.     switch ( drawInfo.mode ) {
  331.     
  332.         case IC_LARGE_BROWSE:
  333.             
  334.             // Draw the icon
  335.             if ( !image ) [self getIcon];
  336.             pt.x = NX_X(cellFrame) + LB_ICON_X;
  337.             pt.y = NX_Y(cellFrame) + NX_HEIGHT(cellFrame) - LB_ICON_Y;
  338.             if ( drawInfo.actualImage && actualImage )
  339.                 [actualImage composite:NX_SOVER toPoint:&pt];
  340.             else
  341.                 [image composite:NX_SOVER toPoint:&pt];
  342.             if ( fileType == FT_APPLICATION && drawInfo.lb_tridots ) {
  343.                 // Draw the dots if it's not running
  344.                 if ( NXPortNameLookup ( appName, "" ) == PORT_NULL ) {
  345.                     pt.x = NX_X(cellFrame) + LB_DOTS_X;
  346.                     pt.y = NX_Y(cellFrame) + NX_HEIGHT(cellFrame ) - LB_DOTS_Y;
  347.                     [dots composite:NX_SOVER toPoint:&pt];
  348.                 }
  349.             }
  350.  
  351.             // Draw the fileStr
  352.             pt.x = NX_X(cellFrame) + LB_PATH_X;
  353.             pt.y = NX_Y(cellFrame) + NX_HEIGHT(cellFrame) - LB_PATH_Y;
  354.             [pathFont set];
  355.             PSsetgray ( NX_BLACK );
  356.             PSshowxy ( pt.x, pt.y, (fileStr ? fileStr : [self fileStr]) );
  357.  
  358.             // Draw the Info Line 1
  359.             if ( drawInfo.lb_info1 ) {
  360.                 infoStr = NULL;
  361.                 switch ( fileType ) {
  362.                     case FT_FILE: // Show the Item's app name
  363.                         if ( specificApp ) infoStr = appName;
  364.                         break;
  365.                     case FT_SUBDIR: // Show the Item's root path
  366.                         if ( createBrowser ) infoStr = rootAt;
  367.                         break;
  368.                     case FT_APPLICATION:
  369.                     case FT_UNKNOWN:
  370.                     default:        break;
  371.                 }
  372.  
  373.                 if ( infoStr ) {
  374.                     pt.x = NX_X(cellFrame) + LB_INFO1_X;
  375.                     pt.y = NX_Y(cellFrame) + NX_HEIGHT(cellFrame) - LB_INFO1_Y;
  376.                     [infoFont set];
  377.                     PSsetgray ( NX_DKGRAY );
  378.                     PSshowxy ( pt.x, pt.y, infoStr );
  379.                 }
  380.             }
  381.             
  382.             break;    // End of IC_LARGE_BROWSE
  383.             
  384.  
  385.         case IC_SMALL_BROWSE:
  386.  
  387.             // Draw the 1/4-size icon
  388.             if ( drawInfo.sb_icon ) {
  389.                 if ( !smallImage ) [self getIcon];
  390.                 pt.x = NX_X(cellFrame) + SB_ICON_X;
  391.                 pt.y = NX_Y(cellFrame)+ NX_HEIGHT(cellFrame) - SB_ICON_Y;
  392.                 [smallImage composite:NX_SOVER toPoint:&pt];
  393.             }
  394.             
  395.             // Draw fileStr
  396.             pt.x = NX_X(cellFrame) + SB_PATH_X;
  397.             pt.y = NX_Y(cellFrame)+ NX_HEIGHT(cellFrame) - SB_PATH_Y;
  398.             [pathFont set];
  399.             PSsetgray ( NX_BLACK );
  400.             PSshowxy ( pt.x, pt.y, (fileStr ? fileStr : [self fileStr]) );
  401.  
  402.             break;  // End of IC_SMALL_BROWSE
  403.         
  404.         
  405.         case IC_LARGE_ICON:
  406.             // Not Implemented
  407.             break; // End of IC_LARGE_ICON
  408.             
  409.             
  410.         case IC_SMALL_ICON:
  411.             // Not Implemented
  412.             break; // End of IC_SMALL_ICON
  413.  
  414.     }
  415.         
  416.     needsUpdate = NO;
  417.     return ( self );
  418. }
  419.  
  420.  
  421.  
  422. - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
  423. {
  424.     cFlags1.state = cFlags1.highlighted = (int)flag;
  425.     [self drawInside:cellFrame inView:controlView];
  426.     return ( self );
  427. }
  428.  
  429.  
  430.  
  431. - (BOOL)needsUpdate
  432. {
  433.     return ( needsUpdate );
  434. }
  435.  
  436.  
  437.  
  438. - (int)state
  439. /*
  440.     Returns TRUE if the cell is selected or highlighted, FALSE otherwise
  441. */
  442. {
  443.     return ( cFlags1.state | cFlags1.highlighted );
  444. }
  445.  
  446.  
  447.  
  448. // -------------------------------------------------------------------------
  449. //   Tracking
  450. // -------------------------------------------------------------------------
  451.  
  452.  
  453. - (BOOL)mouseDownAt:(const NXPoint *)startPt
  454.     frame:(const NXRect *)cellFrame
  455.     inView:controlView;
  456. /*
  457.     Called when a mouseDown: occurs within our frame.  Currently does nothing.  This might be used in the future to initiate direct editing of the path, appName, etc.
  458. */
  459. {
  460.     return ( NO );
  461. }
  462.  
  463.  
  464.  
  465. // -------------------------------------------------------------------------
  466. //   Setting Cell Attributes
  467. // -------------------------------------------------------------------------
  468.  
  469.  
  470. - setPath:(const char *)aPath
  471. /*
  472.     Set the path to aPath
  473. */
  474. {
  475.     NO_LONGER_STATIC;
  476.     
  477.     if ( !path || strcmp ( path, aPath ) ) { // Are we changing anything?
  478.  
  479.         [self resetFileStr];
  480.         if ( path ) NX_FREE ( path );
  481.         
  482.         path = aPath ? NXCopyStringBuffer ( aPath ) : NULL;
  483.  
  484.         // Invalidate icons...
  485.         image = nil;
  486.         actualImage = nil;
  487.         smallImage = nil;
  488.         
  489.         [group setChanged:YES];
  490.         [group setNeedsShow:YES];
  491.     }
  492.  
  493.     return ( self );
  494. }
  495.  
  496.  
  497.  
  498. - resetFileStr
  499. /*
  500.     Reset the Item's fileStr to NULL so that it will be redetermined the next time it is drawn (actaully, the next time -fileStr gets called, which usually happens at draw time)
  501. */
  502. {
  503.     if ( fileStr && fileStr != path ) NX_FREE ( fileStr );
  504.     fileStr = NULL;
  505.     needsUpdate = YES;
  506.     return ( self );
  507. }
  508.  
  509.  
  510.  
  511. - setAutoLaunch:(BOOL)flag
  512. /*
  513.     Set the AutoLaunch flag as specified
  514. */
  515. {
  516.     NO_LONGER_STATIC;
  517.     
  518.     if ( isAutoLaunch != flag ) {
  519.         isAutoLaunch = flag;
  520.         [group setChanged:YES];
  521.         [group setNeedsShow:YES];
  522.     }
  523.     
  524.     return ( self );
  525. }
  526.  
  527.  
  528.  
  529. - setGroupLaunch:(BOOL)flag
  530. /*
  531.     Set the GroupLaunch flag as specified
  532. */
  533. {
  534.     NO_LONGER_STATIC;
  535.     
  536.     if ( isGroupLaunch != flag ) {
  537.         isGroupLaunch = flag;
  538.         [group setChanged:YES];
  539.         [group setNeedsShow:YES];
  540.     }
  541.     
  542.     return ( self );
  543. }
  544.  
  545.  
  546.  
  547. - getInfo
  548. /*
  549.     This method gets the item's file info from WM, which consists of its application, its type, and its extenstion.
  550. */
  551. {
  552.     char *app;
  553.     id ws = [Application workspace];
  554.     NXAtom typ;
  555.     BOOL    ok;
  556.  
  557.     if ( !(path && strlen ( path )) )
  558.         return ( self );
  559.         
  560.     fileType = FT_UNKNOWN;
  561.     
  562.     ok = [ws getInfoForFile:path    // Request file info from workspace
  563.         application:&app
  564.         type:&typ];
  565.         
  566.     if ( ok ) { // WM got the info ok
  567.         
  568.         // Copy the app name
  569.         if ( !specificApp ) {
  570.             if ( appName ) NX_FREE ( appName );
  571.             appName = NXCopyStringBuffer ( app );
  572.         }
  573.                 
  574.         // Determine the file type
  575.         if ( typ == NXDirectoryFileType || typ == NXFilesystemFileType )
  576.             fileType = FT_SUBDIR;
  577.         else
  578.         if ( typ == NXApplicationFileType || typ == NXShellCommandFileType )
  579.             fileType = FT_APPLICATION;
  580.         else
  581.         if ( typ == NXPlainFileType )
  582.             fileType = FT_FILE;
  583.         else
  584.             fileType = FT_FILE; // = FT_UNKNOWN;
  585.     }
  586.     
  587.     return ( self );
  588. }
  589.  
  590.  
  591.     
  592. - getIcon
  593. /*
  594.     Get the icon for path.  First it looks in the CustomIcons directory.  Tiff images in this directory should be called [YY.<large|small>.tiff], where YY is the extension (e.g. 'h', 'm', 'snd'), and large means a normal (48x48) icon and small means a 1/4-size (24x24) icon.  If it doesn't find a matching icon in the CustomIcons dir, it then tries to get it's icon from Workspace Manager.
  595. */
  596. {
  597.     NXSize size;
  598.     NXImageRep *rep;
  599.     char *e, extension[50];
  600.     char iconPath[MAXPATHLEN+1], iconName[50];
  601.     id ws = [Application workspace];
  602.     NXImage    *tempImage;
  603.     
  604.     
  605.     if ( !(path && strlen ( path )) )
  606.         return ( self );
  607.  
  608.     /*
  609.         Get the item's normal-size icon image:
  610.             first look for it by extenstion in the ICONS_DIR, then get
  611.             it from WM if not found
  612.     */
  613.     e = rindex ( path, '.' );
  614.     if ( e ) e++;
  615.     strcpy ( extension, (e ? e : "") );
  616.     
  617.     image = smallImage = nil;
  618.  
  619.     if ( fileType == FT_FILE ) {
  620.         sprintf ( iconName, "%s.large.tiff", extension );
  621.         sprintf ( iconPath, "%s/%s/%s/%s",
  622.             NXHomeDirectory(), LIBRARY_DIR, ICONS_DIR, iconName );
  623.     } else {
  624.         strcpy ( iconName, path );
  625.         strcpy ( iconPath, "" );
  626.     }
  627.     
  628.     // Look and see if the image has been loaded before
  629.     // If it has been loaded before, use it
  630.     if ( !(image = (id)[iconTable valueForKey:iconName]) ) {
  631.     
  632.         // Hasn't been loaded before, look in ICONS_DIR
  633.         if ( strlen ( iconPath ) && access(iconPath, R_OK) != -1 )
  634.             image = [[NXImage alloc] initFromFile:iconPath];
  635.         if ( image ) {
  636.             // Make sure the icon is the right size (48x48)
  637.             size.width = 48.0;
  638.             size.height = 48.0;
  639.             [[image setSize:&size] setScalable:YES];
  640.         } else {
  641.         
  642.             // Couldn't find a custom icon, get it from the workspace
  643.             tempImage = [ws getIconForFile:path];
  644.             if ( tempImage ) {    // Got the icon ok    
  645.                 image = tempImage;
  646.                 [image setDataRetained:YES];
  647.             }
  648.         }
  649.     }
  650.  
  651.     if ( !image ) // Unable to find an icon for this item
  652.         return ( nil );
  653.         
  654.     // Put this image in the iconTable so it can be used again by other
  655.     // items with the same extension
  656.     if ( fileType == FT_FILE )
  657.         [iconTable insertKey:NXUniqueString(iconName) value:image];
  658.     
  659.     // If the item is a tiff or eps image, get a scaled representation
  660.     // of it as its image
  661.     if ( !strcmp ( extension, "tiff" ) || !strcmp ( extension, "eps" ) )
  662.         if ( (actualImage = [[NXImage alloc] initFromFile:path]) ) {
  663.             [actualImage getSize:&size];
  664.             if ( !size.width || (size.width > 24.0) )
  665.                 size.width = 48.0;
  666.             if ( !size.height || (size.height > 24.0) )
  667.                 size.height = 48.0;
  668.             [[actualImage setSize:&size] setScalable:YES];
  669.         }
  670.                     
  671.     
  672.     /*
  673.         Get the item's 1/4-size icon image, using the same process as
  674.         for the normal icons
  675.     */
  676.     
  677.     if ( fileType == FT_FILE ) {
  678.         sprintf ( iconName, "%s.small.tiff", extension );
  679.         sprintf ( iconPath, "%s/%s/%s/%s",
  680.             NXHomeDirectory(), LIBRARY_DIR, ICONS_DIR, iconName );
  681.     } else {
  682.         strcpy ( iconName, path );
  683.         strcpy ( iconPath, "" );
  684.     }
  685.     
  686.     // Look and see if the image has been loaded before
  687.     // If it has been loaded before, use it
  688.     if ( !(smallImage = (id)[iconTable valueForKey:iconName]) ) {
  689.     
  690.         // Hasn't been loaded before, look in ICONS_DIR
  691.         if ( strlen ( iconPath ) && access(iconPath, R_OK) != -1 )
  692.             smallImage = [[NXImage alloc] initFromFile:iconPath];
  693.         if ( smallImage ) {
  694.             // Make sure the icon is the right size (24x24)
  695.             size.width = 24.0;
  696.             size.height = 24.0;
  697.             [[smallImage setSize:&size] setScalable:YES];
  698.         } else {
  699.  
  700.             // Couldn't find a custom icon
  701.             // Copy the full-size icon and scale it down
  702.             size.width = 24.0;
  703.             size.height = 24.0;
  704.             smallImage = [[NXImage alloc] initSize:&size];
  705.             [smallImage setScalable:YES];
  706.             rep = [[image bestRepresentation] copy];
  707.             [smallImage useRepresentation:rep];
  708.             [rep setSize:&size];
  709.         }
  710.     }
  711.         
  712.     if ( !smallImage ) // Unable to find an icon for this item
  713.         return ( nil );
  714.     
  715.     // Put this image in the iconTable so it can be used again by other
  716.     // items with the same extension
  717.     if ( fileType == FT_FILE )
  718.         [iconTable insertKey:NXUniqueString(iconName) value:smallImage];
  719.     
  720.     
  721.     return ( self );
  722. }
  723.  
  724.  
  725.  
  726. - (const char *)path
  727. {
  728.     return ( path );
  729. }
  730.  
  731.  
  732.  
  733. - (int)fileType
  734. /*
  735.     Returns the type of file this item represents -- e.g. an application, a folder
  736. */
  737. {
  738.     if ( fileType == FT_UNKNOWN ) [self getInfo];
  739.     return ( fileType );
  740. }
  741.  
  742.  
  743.  
  744. - (const char *)fileStr
  745. /*
  746.     Returns a formatted string using the nameMode of the current drawInfo
  747. */
  748. {
  749.     char str[MAXPATHLEN+1];
  750.     char *s, *ch, *home;
  751.     const char *groupPath;
  752.     
  753.     switch ( nameMode ) {
  754.         case IC_FULLPATH: // /Users/joe/Apps/Checkers.app
  755.             fileStr = path;
  756.             break;
  757.             
  758.         case IC_FILEANDPATH: // Checkers.app -- /Users/joe/Apps
  759.             s = rindex ( path, '/' ); // Find last path separator
  760.             if ( s && s != path ) {
  761.                 ch = s;
  762.                 *ch = '\0'; // Temporarily break string
  763.                 s++;
  764.                 if ( strstr ( path, NXHomeDirectory() ) == path ) {
  765.                     home = &path[strlen(NXHomeDirectory())];
  766.                     sprintf ( str, "%s -- ~%s", s, home );
  767.                 } else
  768.                     sprintf ( str, "%s -- %s", s, !(*path) ? "/" : path );
  769.                 *ch = '/'; // Restore original string
  770.             } else
  771.                 sprintf ( str, path );
  772.             fileStr = NXCopyStringBuffer ( str );
  773.             break;
  774.             
  775.         case IC_FILEONLY: // Checkers.app
  776.             s = rindex ( path, '/' ); // Find last path separator
  777.             if ( s && s != path ) {
  778.                 ch = s;
  779.                 *ch = '\0'; // Temporarily break string
  780.                 s++;
  781.                 fileStr = NXCopyStringBuffer ( s );
  782.                 *ch = '/'; // Restore the original string
  783.             } else
  784.                 fileStr = path;
  785.             break;
  786.             
  787.         case IC_RELATIVE: // Apps/Checkers.app for /Users/joe
  788.             groupPath = [group defaultPath];
  789.             if ( groupPath && strstr ( path, groupPath ) && strcmp ( path, groupPath ) ) {
  790.                 s = path + strlen(groupPath) + 1; // Point after the '/'
  791.                 fileStr = NXCopyStringBuffer ( s );
  792.             } else
  793.                 fileStr = path;
  794.             break;
  795.             
  796.         case IC_NOPATH: // Don't display anything
  797.             fileStr = NXCopyStringBuffer ( "" );
  798.             break;
  799.             
  800.         case IC_NAME_UNKNOWN:
  801.         default:
  802.             fileStr = path;
  803.             break;
  804.     }    
  805.             
  806.     return ( fileStr );
  807. }
  808.  
  809.  
  810.  
  811. - (const char *)filename
  812. /* Returns the last component of the Item's path -- everything after the last '/' */
  813. {
  814.     char *s;
  815.     if ( !path ) return ( NULL );
  816.     s = rindex ( path, '/' );
  817.     if ( s ) s++;
  818.         else s = path;
  819.     return ( s );
  820. }
  821.  
  822.  
  823.  
  824. - (BOOL)isAutoLaunch
  825. {
  826.     return ( isAutoLaunch );
  827. }
  828.  
  829.  
  830.  
  831. - (BOOL)isGroupLaunch
  832. {
  833.     return ( isGroupLaunch );
  834. }
  835.  
  836.  
  837.  
  838. - (NXImage *)image
  839. /*
  840.     Return the NXImage of the item's icon
  841. */
  842. {
  843.     if ( !image ) [self getIcon];
  844.     return ( image );
  845. }
  846.  
  847.  
  848.  
  849. - (NXImage *)smallImage
  850. /*
  851.     Returns the NXImage used to draw the item's 1/4-size icon
  852. */
  853. {
  854.     return ( smallImage );
  855. }
  856.  
  857.  
  858.  
  859. // -------------------------------------------------------------------------
  860. //   Group
  861. // -------------------------------------------------------------------------
  862.  
  863.  
  864. - setGroup:aGroup
  865. {
  866.     group = aGroup;
  867.     [self resetFileStr];
  868.     return ( self );
  869. }
  870.  
  871.  
  872.  
  873. - group
  874. {
  875.     return ( group );
  876. }
  877.  
  878.  
  879.  
  880. // -------------------------------------------------------------------------
  881. //   File-type Items
  882. // -------------------------------------------------------------------------
  883.  
  884.  
  885. - setAppName:(const char *)aString
  886. /*
  887.     Set the name of the app used to open the item.  If this method is never called on an item, it uses the default app as determined by Workspace.  If aString is NULL or an empty string, it goes back to using the default.
  888. */
  889. {
  890.     NO_LONGER_STATIC;
  891.     
  892.     if ( aString && strlen ( aString ) ) {
  893.         if ( !specificApp || strcmp ( appName, aString ) )
  894.             [group setChanged:YES];
  895.             
  896.         // Set a specific app
  897.         specificApp = YES;
  898.         
  899.         // Copy the app's name
  900.         appName = NXCopyStringBuffer ( aString );
  901.     } else {
  902.         if ( specificApp ) [group setChanged:YES];
  903.         
  904.         // Use the default app
  905.         specificApp = NO;
  906.     }
  907.     
  908.     [self getIcon];
  909.     [group setNeedsShow:YES];
  910.     return ( self );
  911. }
  912.  
  913.  
  914.  
  915. - (const char *)appName
  916. {
  917.     return ( appName );
  918. }
  919.  
  920.  
  921.  
  922. - (BOOL)hasSpecificApp
  923. /*
  924.     Returns NO if the Item will use the default application determined by Workspace, or YES if it will use a specific (user-specified) app
  925. */
  926. {
  927.     return ( specificApp );
  928. }
  929.  
  930.  
  931.  
  932. // -------------------------------------------------------------------------
  933. //   Subdir-type Items
  934. // -------------------------------------------------------------------------
  935.  
  936.  
  937. - setCreateBrowser:(BOOL)flag
  938. /*
  939.     If flag is YES, then when this subdir is opened in Workspace, a new FileViewer will be created to display it.  The new Viewer will be rooted at the path contained in rootAt.
  940. */
  941. {
  942.     NO_LONGER_STATIC;
  943.     
  944.     if ( createBrowser != flag ) {
  945.         createBrowser = flag;
  946.         [group setChanged:YES];
  947.         [group setNeedsShow:YES];
  948.     }
  949.  
  950.     return ( self );
  951. }
  952.  
  953.  
  954.  
  955. - rootBrowserAt:(const char *)fullPath
  956. /*
  957.     Sets the path that will be the root directory for the FileViewer created in Workspace Manager when this item is opened.  This only applies if createBrowser is true
  958. */
  959. {
  960.     NO_LONGER_STATIC;
  961.     
  962.     if ( rootAt != fullPath ||
  963.         (rootAt && fullPath && strcmp ( rootAt, fullPath )) ) {
  964.         
  965.         if ( rootAt ) NX_FREE ( rootAt );
  966.         
  967.         rootAt = fullPath ? NXCopyStringBuffer ( fullPath ) : NULL;
  968.         [group setChanged:YES];
  969.         [group setNeedsShow:YES];
  970.     }
  971.     
  972.     return ( self );
  973. }
  974.  
  975.  
  976.  
  977. - (BOOL)createBrowser
  978. {
  979.     return ( createBrowser );
  980. }
  981.  
  982.  
  983.  
  984. - (const char *)browserRootedAt
  985. {
  986.     return ( rootAt );
  987. }
  988.  
  989.  
  990.  
  991. // -------------------------------------------------------------------------
  992. //   App-type Items
  993. // -------------------------------------------------------------------------
  994.  
  995.  
  996. - setHideIcon:(BOOL)flag
  997. /*
  998.     if flag is YES, then when the Application is launched, it's Icon will be hidden.  If NO, its icon will show as it normally does
  999. */
  1000. {
  1001.     NO_LONGER_STATIC;
  1002.     
  1003.     if ( hideIcon != flag ) {
  1004.         hideIcon = flag;
  1005.         [group setChanged:YES];
  1006.         [group setNeedsShow:YES];
  1007.     }
  1008.  
  1009.     return ( self );
  1010. }
  1011.  
  1012.  
  1013.  
  1014. - (BOOL)hideIcon
  1015. {
  1016.     return ( hideIcon );
  1017. }
  1018.  
  1019.  
  1020.  
  1021. // -------------------------------------------------------------------------
  1022. //   Dynamic Items
  1023. // -------------------------------------------------------------------------
  1024.  
  1025.  
  1026. - setDynamic:(BOOL)flag
  1027. /*
  1028.     A Dynamic Item isn't archived.  It is a run-time only Item created from the Dynamic Item specs in the group. If a dynamic item is changed in any way (ie though the inspector, it has some properties set), then it is automatically set to be static, which means it then becomes a permanent part of the group.
  1029. */
  1030. {
  1031.     if ( dynamic != flag ) {
  1032.         dynamic = flag;
  1033.         if ( dynamic ) { // Setting to be a dynamic item
  1034.         } else { // Going from a dynamic to static item
  1035.             [group setChanged:YES];
  1036.             [group setNeedsShow:YES];
  1037.         }
  1038.     }
  1039.     return ( self );
  1040. }
  1041.  
  1042.  
  1043.  
  1044. - (BOOL)isDynamic
  1045. {
  1046.     return ( dynamic );
  1047. }
  1048.  
  1049.  
  1050.  
  1051. // -------------------------------------------------------------------------
  1052. //   Launching
  1053. // -------------------------------------------------------------------------
  1054.  
  1055.  
  1056. - (BOOL)launch
  1057. /*
  1058.     Opens the item using Workspace Manager.  Returns yes if operation succeeded, else NO
  1059. */
  1060. {
  1061.     id    workspace = [Application workspace];
  1062.     port_t    appPort;
  1063.     
  1064.     if ( !workspace ) {
  1065.         NXRunAlertPanel ( "Launch", "Unable to contact Workspace Manager!",
  1066.             NULL, NULL, NULL );
  1067.         return ( NO );
  1068.     }
  1069.         
  1070.     switch ( [self fileType] ) { // Open according to file type
  1071.         
  1072.         case FT_FILE: // General files
  1073.             if ( specificApp ) {
  1074.                 // Open using specified application
  1075.                 [workspace openFile:path
  1076.                     withApplication:appName
  1077.                     andDeactivate:YES];
  1078.             } else {
  1079.                 // Open using default app
  1080.                 [workspace openFile:path
  1081.                     withApplication:NULL
  1082.                     andDeactivate:YES];
  1083.             }        
  1084.             break;
  1085.         
  1086.         case FT_APPLICATION: // An application
  1087.             if ( (appPort = NXPortNameLookup ( appName, NULL )) != PORT_NULL ) {
  1088.                 // The app is already running, activate it
  1089.                 // activate it by calling Speaker's launchProgram:ok: method
  1090.                 int status, flag;
  1091.                 [[NXApp appSpeaker] setSendPort:
  1092.                     NXPortFromName ( NX_WORKSPACEREQUEST, NULL )];
  1093.                 status = [[NXApp appSpeaker] launchProgram:path ok:&flag];
  1094.             } else  {
  1095.                 [workspace launchApplication:path
  1096.                     showTile:(!hideIcon)
  1097.                     autolaunch:isAutoLaunch];
  1098.             }
  1099.             break;
  1100.             
  1101.         case FT_SUBDIR: // A directory/folder
  1102.             if ( specificApp ) {
  1103.                 // Open using specified application
  1104.                 [workspace openFile:path
  1105.                     withApplication:appName
  1106.                     andDeactivate:YES];
  1107.             } else
  1108.             if ( createBrowser ) {
  1109.                 [workspace selectFile:path
  1110.                     inFileViewerRootedAt:((rootAt && rootAt[0]) ? rootAt : "/")];
  1111.             } else {
  1112.                 [workspace selectFile:path
  1113.                     inFileViewerRootedAt:""];
  1114.             }
  1115.             break;
  1116.             
  1117.         default:
  1118.             return ( NO );
  1119.     }
  1120.  
  1121.     needsUpdate = YES; // Needs to be redrawn to update tri-dots
  1122.     [[self controlView] setNeedsDisplay:YES];
  1123.  
  1124.     return ( YES );
  1125. }
  1126.  
  1127.  
  1128.  
  1129. // -------------------------------------------------------------------------
  1130. //   Archiving
  1131. // -------------------------------------------------------------------------
  1132.  
  1133.  
  1134. - awake
  1135. /*
  1136.     Finish initializing after reading an archived ItemCell
  1137. */
  1138. {
  1139.     [super awake];
  1140.     
  1141.     cFlags1.state = cFlags1.highlighted = 0;
  1142.     
  1143.     if ( path && path[0] != '/' ) [self setPath:"/"];
  1144.     needsUpdate = YES;
  1145.     return ( self );
  1146. }
  1147.  
  1148.  
  1149.  
  1150. - read:(NXTypedStream *)stream
  1151. /*
  1152.     Reads an archived instance from stream, according to it's version number.
  1153. */
  1154. {
  1155.     int versionNumber;
  1156.     
  1157.     [super read:stream];
  1158.     
  1159.     // Get versionNumber
  1160.     versionNumber = NXTypedStreamClassVersion ( stream, [[self class] name] );
  1161.     
  1162.     if ( versionNumber <= ItemCell_VERSION ) { // thru current version
  1163.         NXReadTypes ( stream, "*ccccc**",
  1164.             &path,
  1165.             &isAutoLaunch, &isGroupLaunch, &specificApp,
  1166.             &createBrowser, &hideIcon,
  1167.             &appName,
  1168.             &rootAt );
  1169.         NXReadPoint ( stream, &largeIconLoc );
  1170.         NXReadPoint ( stream, &smallIconLoc );
  1171.     } 
  1172.     
  1173.     else { // Unrecognized archive version
  1174.         [self errMsg:"ItemCell: unknown version %i in archived object!\n",
  1175.             versionNumber];
  1176.     }
  1177.     
  1178.     return ( self );
  1179. }
  1180.  
  1181.  
  1182.  
  1183. - write:(NXTypedStream *)stream
  1184. /*
  1185.     Writes the important instance variables to stream.
  1186. */
  1187. {
  1188.     if ( dynamic )
  1189.         return ( self );    // DynamicItems don't archive
  1190.  
  1191.     [super write:stream];
  1192.  
  1193.     // Write the instance variables    
  1194.     NXWriteTypes ( stream, "*ccccc**",
  1195.         &path,
  1196.         &isAutoLaunch, &isGroupLaunch, &specificApp,
  1197.         &createBrowser, &hideIcon,
  1198.         &appName,
  1199.         &rootAt );
  1200.     NXWritePoint ( stream, &largeIconLoc );
  1201.     NXWritePoint ( stream, &smallIconLoc );
  1202.     
  1203.     return ( self );
  1204. }
  1205.  
  1206.  
  1207.  
  1208. @end
  1209.